﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Neturion.Dados;
using Neturion.IRepositorios;
using Neturion.Repositorios;
using Neturion.Utils.GerenciamentoBoletos;

namespace Neturion.Negocios
{
    public class NegocioTitulo
    {
        private IRepositorioTitulo _repositorioTitulo;
        private NegocioMovimentoFinanceiro _negocioMovimentoFinanceiro;
        private NegocioPagamento _negocioPagamento;

        public NegocioTitulo()
        {
            _repositorioTitulo = new RepositorioTitulo();
        }

        public Titulo Inserir(Titulo titulo, Usuario usuarioPermissao)
        {
            //Validar Titulo
            if (! ValidarTitulo(titulo))
            {
                throw new Exception("Erro Ao Validar Titulo");
            }

            //titulo.NossoNumero = Utils.GerenciamentoBoletos.GeracaoBoleto.CalcularNossoNumero(titulo.NossoNumero, 11);

            titulo.EnumSituacaoMovimento = EnumSituacaoMovimento.Aberto;
            titulo.DataCadastro = DateTime.Today;
            
            //Tirando as Horas das Datas
            Utils.Util.TirarHoraDatasObjetos(ref titulo);

            return _repositorioTitulo.Inserir(titulo);
        }

        public IList<Titulo> GerarTitulosMovimentoFinanceiro(MovimentoFinanceiro movimentoFinanceiro, Usuario usuarioPermissao)
        {
            IList<Titulo> titulos = new List<Titulo>();
            Titulo tituloTmp;
            //Geração dos Titulos de Acordo o Numero de Parcelas do MovimentoFinanceiro
            for (int numeroParcela = 1; numeroParcela <= movimentoFinanceiro.QuantidadeParcelas ; numeroParcela++)
            {
                //CriarTitulos
                tituloTmp = new Titulo();
                //Montar os dados dos Titulos pelo MovimentoFinanceiro
                tituloTmp.DataCadastro = DateTime.Today;
                tituloTmp.DataEmissao = DateTime.Today;
                tituloTmp.Desconto = movimentoFinanceiro.Desconto;
                tituloTmp.DescontoCofins = movimentoFinanceiro.DescontoCofins;
                tituloTmp.DescontoCssl = movimentoFinanceiro.DescontoCssl;
                tituloTmp.DescontoInss = movimentoFinanceiro.DescontoInss;
                tituloTmp.DescontoIr = movimentoFinanceiro.DescontoIr;
                tituloTmp.DescontoIss = movimentoFinanceiro.DescontoIss;
                tituloTmp.Documento = movimentoFinanceiro.Documento;
                tituloTmp.EnumSituacaoMovimento = EnumSituacaoMovimento.Aberto;
                tituloTmp.EnumTipoMovimentoFinanceiro = movimentoFinanceiro.EnumTipoMovimentoFinanceiro;
                tituloTmp.JurosTaxas = movimentoFinanceiro.JurosTaxas;
                tituloTmp.MovimentoFinanceiro = movimentoFinanceiro;
                tituloTmp.Multa = movimentoFinanceiro.Multa;
                tituloTmp.Usuario = usuarioPermissao;
                tituloTmp.ValidadePagamento = movimentoFinanceiro.ValidadePagamento;
                
                /*
                //Nome do Credor/Devedo na Descrição do Titulo
                tituloTmp.Descricao = !string.IsNullOrEmpty(movimentoFinanceiro.PessoaCredorDevedor.NomeReduzido)
                                      ? movimentoFinanceiro.PessoaCredorDevedor.NomeReduzido
                                      : movimentoFinanceiro.PessoaCredorDevedor.NomeRazaoSocial;
                
                //Descrição do Movimento na descrição dos Titulos
                tituloTmp.Descricao = tituloTmp.Descricao 
                                      + "/" 
                                      + movimentoFinanceiro.Descricao;
                 */
               
                tituloTmp.Descricao = movimentoFinanceiro.Descricao;

                //Historico
                tituloTmp.Historico = movimentoFinanceiro.Historico;
                
                //se For parcelado coloca o número da parcela na Descrição e no Historico e o Valor das Parcelas
                if (movimentoFinanceiro.Parcelado)
                {
                    //Descrição
                    tituloTmp.Descricao = tituloTmp.Descricao
                        + " Parcela Nº " 
                        + numeroParcela 
                        + " de "
                        + movimentoFinanceiro.QuantidadeParcelas;
                    
                    //Historico
                    tituloTmp.Historico = tituloTmp.Historico + Environment.NewLine 
                        + " Parcela Nº " 
                        + numeroParcela
                        + " de "
                        + movimentoFinanceiro.QuantidadeParcelas;
                    
                    //Valor Parcelado
                    tituloTmp.Valor = decimal.Round((movimentoFinanceiro.Valor/movimentoFinanceiro.QuantidadeParcelas),2);
                }
                else
                {
                    //Valor Cheio
                    tituloTmp.Valor = movimentoFinanceiro.Valor;
                }

                //Se for a primeira parcela, o vencimento é a do MovimentoFinanceiro senão Incrementa, a partir da Segunda Rodada por isso é -1 
                tituloTmp.DataVencimento = numeroParcela == 1 ?
                                           movimentoFinanceiro.DataVencimento :
                                           movimentoFinanceiro.DataVencimento.AddMonths(numeroParcela-1);
                
                //Adiciona na Lista de Titulos que vai ser retornada
                titulos.Add(tituloTmp);
            }

            //Colocando o Valor do Ultimo Titulo com os Quebrados Somente em Titulos Que forma Parcelados
            if (movimentoFinanceiro.Parcelado)
            {
                titulos[titulos.Count - 1].Valor = movimentoFinanceiro.Valor -
                                               (decimal.Round((movimentoFinanceiro.Valor / movimentoFinanceiro.QuantidadeParcelas), 2) *
                                               (movimentoFinanceiro.QuantidadeParcelas - 1));
            }
            
            return titulos as List<Titulo>;
        }

        public Titulo Atualizar(Titulo titulo, Usuario usuarioPermissao)
        {
            //Validar Titulo
            if (!ValidarTitulo(titulo))
            {
                throw new Exception("Erro Ao Validar Titulo");
            }

            //Tirando as Horas das Datas
            Utils.Util.TirarHoraDatasObjetos(ref titulo);

            return _repositorioTitulo.Atualizar(titulo);
        }

        public Titulo AlterarValorVencimentoTitulo(Titulo titulo, Usuario usuarioPermissao)
        {
            Titulo tituloOriginal = ObterPorId(titulo.MovimentoId);
            MovimentoFinanceiro movimentoFinanceiroOriginal = tituloOriginal.MovimentoFinanceiro;
            _negocioMovimentoFinanceiro = new NegocioMovimentoFinanceiro();
            
            decimal valorSaldoMovimento = _negocioMovimentoFinanceiro.ValorSaldoMovimento(movimentoFinanceiroOriginal);
            bool temTituloAberto = valorSaldoMovimento > 0;
            int qtdParcelasEmAberto =
                _negocioMovimentoFinanceiro.QuantidadeTitulosAbertosMovimento(movimentoFinanceiroOriginal);

            //Tratamento para quando o Movimento só tem duas parcelas
            qtdParcelasEmAberto = (qtdParcelasEmAberto > 1 ? qtdParcelasEmAberto - 1 : 1);

            //Validar Titulo
            if (!ValidarTitulo(titulo))
            {
                throw new Exception("Erro Ao Validar Titulo");
            }

            if (!temTituloAberto)
            {
                throw new Exception("Erro Ao Validar Titulo, Sem Titulos em Aberto para Alterar");
            }

            if (titulo.EnumSituacaoMovimento != EnumSituacaoMovimento.Aberto)
            {
                throw new Exception("Situação do Título não permite Alteração de Valor e Vencimento");
            }

            if (movimentoFinanceiroOriginal.EnumSituacaoMovimento == EnumSituacaoMovimento.Extornado)
            {
                throw new Exception("Situação do Movimento não permite Alteração de Valor e Vencimento");
            }

            if (movimentoFinanceiroOriginal.EnumSituacaoMovimento == EnumSituacaoMovimento.Quitado)
            {
                throw new Exception("Situação do Movimento não permite Alteração de Valor e Vencimento");
            }
            
            if ((!movimentoFinanceiroOriginal.Parcelado)&&(titulo.Valor != tituloOriginal.Valor))
            {
                throw new Exception("Só permitido alterar Valor de Títulos parcelados");
            }

            if (titulo.DataVencimento < movimentoFinanceiroOriginal.DataEmissao)
            {
                throw new Exception("Só permitido alterar Vencimento para após a Emissão do Movimento");
            }

            if (titulo.ListaPagamentos.Count > 0)
            {
                throw new Exception("Só permitido alterar Títulos Sem Pagamentos Vínculados");
            }

            if (titulo.Valor > movimentoFinanceiroOriginal.Valor)
            {
                throw new Exception("Valor do Título Maior que o Valor da Movimentação Financeira");
            }

            if(titulo.Valor > valorSaldoMovimento - qtdParcelasEmAberto)
            {
                throw new Exception("Valor do Título Maior que o Saldo Movimentavel da Movimentação Financeira");
            }

            //Diferença do titulo a ser dividida entre as parcelas
            decimal diferencaTitulo = tituloOriginal.Valor - titulo.Valor;

            //Calcula o valor de cada parcela
            decimal valorSaldoAlteradoTituloParcela = decimal.Round((diferencaTitulo / qtdParcelasEmAberto), 2);

            //Calcula o Valor da Ultima parcela
            decimal valorSaldoAlteradoTituloUltimaParcela = diferencaTitulo - decimal.Round((valorSaldoAlteradoTituloParcela * (qtdParcelasEmAberto - 1)), 2);

            //divide o valor entre as parcelas e Pega o Valor residual caso alguma parcela não comporte a diferença
            decimal valorResidual = DividirValorSaldo(movimentoFinanceiroOriginal, titulo, usuarioPermissao,
                                                       qtdParcelasEmAberto, valorSaldoAlteradoTituloParcela,
                                                       valorSaldoAlteradoTituloUltimaParcela);
            
            //Pega alguma diferença de valores entre os saldos (o saldo do titulo alterado) - (o saldo do titulo original) e joga denovo nas parcelas
            decimal valorDiferencaSaldos = _negocioMovimentoFinanceiro.ValorSaldoMovimento(movimentoFinanceiroOriginal) - _negocioMovimentoFinanceiro.ValorSaldoMovimento(tituloOriginal.MovimentoFinanceiro);
            if ((valorDiferencaSaldos != 0) && (valorResidual == 0))
            {
                valorResidual += valorDiferencaSaldos;
            }

            //Pegar valores de parcelas que não comportam as diferenças
            while (valorResidual != 0)
            {
                //Calcula o valor de cada parcela
                valorSaldoAlteradoTituloParcela = decimal.Round((valorResidual / qtdParcelasEmAberto), 2);
                //Calcula o Valor da Ultima parcela
                valorSaldoAlteradoTituloUltimaParcela = valorResidual - decimal.Round((valorSaldoAlteradoTituloParcela * (qtdParcelasEmAberto - 1)), 2);
                //Pega o Valor residual caso alguma parcela não comporte a diferença
                valorResidual = DividirValorSaldo(movimentoFinanceiroOriginal, titulo, usuarioPermissao,
                                                       qtdParcelasEmAberto, valorSaldoAlteradoTituloParcela,
                                                       valorSaldoAlteradoTituloUltimaParcela);

                //Pegar alguma diferença de valores entre os saldos (o saldo do titulo alterado) - (o saldo do titulo original) e joga denovo nas parcelas
                valorDiferencaSaldos = _negocioMovimentoFinanceiro.ValorSaldoMovimento(movimentoFinanceiroOriginal) - _negocioMovimentoFinanceiro.ValorSaldoMovimento(tituloOriginal.MovimentoFinanceiro);
                if ((valorDiferencaSaldos != 0) && (valorResidual == 0))
                {
                    valorResidual += valorDiferencaSaldos;
                }
            }

            return movimentoFinanceiroOriginal.ListaTitulos.Cast<Titulo>().ToList().First((tituloLista => (tituloLista.MovimentoId == titulo.MovimentoId)));
        }

        private decimal DividirValorSaldo(MovimentoFinanceiro movimentoFinanceiroOriginal, Titulo titulo, Usuario usuarioPermissao, int qtdParcelasEmAberto, decimal valorSaldoAlteradoTituloParcela, decimal valorSaldoAlteradoTituloUltimaParcela)
        {
            decimal valorResidual = 0;
            int parcela = 0;
            foreach (Titulo tituloMovimento in movimentoFinanceiroOriginal.ListaTitulos)
            {

                if (tituloMovimento.EnumSituacaoMovimento == EnumSituacaoMovimento.Aberto)
                {
                    parcela++;
                    if (tituloMovimento.MovimentoId == titulo.MovimentoId)
                    {
                        tituloMovimento.Valor = titulo.Valor;
                        tituloMovimento.DataVencimento = titulo.DataVencimento;
                        titulo = Atualizar(tituloMovimento, usuarioPermissao);
                    }
                    else
                    {
                        //Se for a Ultima parcela
                        if (parcela != qtdParcelasEmAberto)
                        {
                            tituloMovimento.Valor += valorSaldoAlteradoTituloParcela;
                            if (tituloMovimento.Valor > 0)
                            {
                                valorResidual += 0;
                            }
                            else
                            {
                                valorResidual += tituloMovimento.Valor - 1;
                                tituloMovimento.Valor = 1;
                            }
                            
                            Atualizar(tituloMovimento, usuarioPermissao);
                        }
                        else
                        {
                            tituloMovimento.Valor += valorSaldoAlteradoTituloUltimaParcela;
                            if (tituloMovimento.Valor > 0)
                            {
                                valorResidual += 0;
                            }
                            else
                            {
                                valorResidual += tituloMovimento.Valor - 1;
                                tituloMovimento.Valor = 1;
                            }

                            Atualizar(tituloMovimento, usuarioPermissao);
                        }
                    }
                }
            }
            return valorResidual;
        }

        public bool ValidarTitulo(Titulo titulo)
        {
            Boolean retorno = true;

            if (titulo == null)
            {
                retorno = false;
            }
            else
            {
                if (titulo.Valor == 0)
                {
                    retorno = false;
                }

                if (titulo.MovimentoFinanceiro == null)
                {
                    if (titulo.MovimentoFinanceiro == null)
                    {
                        retorno = false;
                    }
                }
            }
            
            return retorno;
        }

        public decimal ValorPagamentosTitulo(Titulo titulo)
        {
            //decimal valorTotalPagamentos = 0;

            //Validar Titulo
            if (!ValidarTitulo(titulo))
            {
                throw new Exception("Erro Ao Validar Titulo");
            }

            //if(titulo.ListaPagamentos != null)
            //{
            //    foreach (Pagamento pagamento in titulo.ListaPagamentos)
            //    {
            //        //So Soma os Pagamentos Quitados (Não os Estornados)
            //        if (pagamento.EnumSituacaoMovimento == EnumSituacaoMovimento.Quitado)
            //        {
            //            valorTotalPagamentos += pagamento.Valor;
            //        }
            //    }
            //}

            //return valorTotalPagamentos;
            _negocioPagamento = new NegocioPagamento();
            return _negocioPagamento.ObterPagamentosTitulo(titulo);
        }

        public decimal ValorTituloHoje(Titulo titulo)
        {
            decimal valorTotalTitulo=0;

            //Validar Titulo
            if (!ValidarTitulo(titulo))
            {
                throw new Exception("Erro Ao Validar Titulo");
            }

            switch(titulo.EnumSituacaoMovimento)  
            {
                //Se Tiver aberto ou Parcialmente Quitado Calcula o Valor do Titulo
                case EnumSituacaoMovimento.Aberto:
                case EnumSituacaoMovimento.ParcialmenteQuitado:
                {
                    valorTotalTitulo = titulo.Valor;
                    
                    decimal valorJuros = 0;
                    decimal valorMulta = 0;
                    decimal valorDesconto = 0;

                    DateTime dataHoje = DateTime.Now.Date; 
                    int diaHoje = dataHoje.Day;
                    int mesHoje = dataHoje.Month;
                    int anoHoje = dataHoje.Year;

                    decimal valorPagamentosTitulo = ValorPagamentosTitulo(titulo);

                    //Percentual de Juros por Dia = Taxa de Juros Dividido pela quantidade de Dias do Mês
                    decimal jurosDia = titulo.JurosTaxas.GetValueOrDefault() / DateTime.DaysInMonth(anoHoje,mesHoje);

                    //se o Titulo ta Vencido ou não
                    if (titulo.DataVencimento.Date < dataHoje)
                    {
                        //Quantidade de Dias em Atraso do Titulo
                        int diasAtraso = (dataHoje - titulo.DataVencimento.Date).Days;

                        //Valor do juros Calculado em cima do Saldo do Titulo e dos Dias em Atraso
                        valorJuros = (((valorTotalTitulo - valorPagamentosTitulo)/100) * (diasAtraso * jurosDia));

                        //Valor da Multa Calculada sobre o valor do Saldo do Titulo
                        valorMulta = (((valorTotalTitulo - valorPagamentosTitulo) / 100) * titulo.Multa.GetValueOrDefault());

                        //Valor do Titulo = Valor Juros e Multa
                        valorTotalTitulo += valorJuros + valorMulta;

                    }
                    else
                    {
                        //Calculo do Valor do Desconto em cima do saldo restante
                        valorDesconto = (((valorTotalTitulo - valorPagamentosTitulo) / 100) * titulo.Desconto.GetValueOrDefault());
                        
                        //Valor do Titulo = Valor Titulo - Desconto
                        valorTotalTitulo -= valorDesconto;
                    }

                    break;
                }

                //Se ta Extornado é informado o Valor do Titulo
                case EnumSituacaoMovimento.Extornado:
                {
                    valorTotalTitulo = titulo.Valor;
                    break;
                }

                //Se ta quitado é informado o Valor do Pagamento
                case EnumSituacaoMovimento.Quitado:
                {
                    valorTotalTitulo = ValorPagamentosTitulo(titulo);
                    break;
                }

                default:
                {
                    throw new Exception("Situação Invalida do Titulo");
                    break;
                }

            }
            return valorTotalTitulo;
        }

        public decimal ValorSaldoTitulo(Titulo titulo)
        {
            decimal valorSaldoTitulo = 0;

            //Validar Titulo
            if (!ValidarTitulo(titulo))
            {
                throw new Exception("Erro Ao Validar Titulo");
            }

            valorSaldoTitulo = (ValorTituloHoje(titulo) - ValorPagamentosTitulo(titulo));
            return valorSaldoTitulo;
        }

        public Titulo QuitarTitulo(Titulo titulo, Pagamento pagamento, Usuario usuarioPermissao)
        {
            MovimentoFinanceiro movimentoFinanceiroTmp = Utils.Util.Clonar(titulo.MovimentoFinanceiro, null, null);
            //Retira o Titulo da Lista e Faz as Alterações
            movimentoFinanceiroTmp.RemoverTitulo(titulo);

            _negocioMovimentoFinanceiro = new NegocioMovimentoFinanceiro();

            //Validar Titulo
            if (!ValidarTitulo(titulo))
            {
                throw new Exception("Erro Ao Validar Titulo");
            }
            
            //Se o Pagamento quita o Titulo ou não
            if (pagamento.Valor >= ValorSaldoTitulo(titulo))
            {
                //Situação de quitado
                titulo.EnumSituacaoMovimento = EnumSituacaoMovimento.Quitado;

                //Data da quitação
                titulo.DataQuitado = DateTime.Now.Date;
            }
            else
            {
                //Situação de quitado
                titulo.EnumSituacaoMovimento = EnumSituacaoMovimento.ParcialmenteQuitado;

                //Data da quitação
                titulo.DataQuitado = null;
            }

            movimentoFinanceiroTmp.AdicionarTitulo(titulo);

            return _negocioMovimentoFinanceiro.QuitarMovimentoFinanceiro(movimentoFinanceiroTmp, usuarioPermissao).ListaTitulos[movimentoFinanceiroTmp.ListaTitulos.IndexOf(titulo)] as Titulo;
        }

        public Titulo AbrirTitulo(Titulo titulo, Pagamento pagamento, Usuario usuarioPermissao)
        {
            MovimentoFinanceiro movimentoFinanceiroTmp = Utils.Util.Clonar(titulo.MovimentoFinanceiro, null, null);
            //Retira o Titulo da Lista e Faz as Alterações
            movimentoFinanceiroTmp.RemoverTitulo(titulo);

            _negocioMovimentoFinanceiro = new NegocioMovimentoFinanceiro();
            //Validar Titulo
            if (!ValidarTitulo(titulo))
            {
                throw new Exception("Erro Ao Validar Titulo");
            }
            
            titulo.EnumSituacaoMovimento = ValorPagamentosTitulo(titulo) - pagamento.Valor > 0 ? EnumSituacaoMovimento.ParcialmenteQuitado : EnumSituacaoMovimento.Aberto;

            titulo.DataQuitado = null;

            movimentoFinanceiroTmp.AdicionarTitulo(titulo);

            return _negocioMovimentoFinanceiro.AbrirMovimentoFinanceiro(movimentoFinanceiroTmp, usuarioPermissao).ListaTitulos[movimentoFinanceiroTmp.ListaTitulos.IndexOf(titulo)] as Titulo; 
        }

        public Titulo EstornarTitulo(Titulo titulo, Usuario usuarioPermissao)
        {
            //Titulo tituloTmp;

            if (!ValidarTitulo(titulo))
            {
                throw new Exception("Erro ao Validar Título!");
            }

            if ((titulo.EnumSituacaoMovimento == EnumSituacaoMovimento.Inserindo)
              || (titulo.EnumSituacaoMovimento == EnumSituacaoMovimento.Extornado))
            {
                throw new Exception("Título está com a Situação de " + titulo.EnumSituacaoMovimento.ToString()+ ", NÃO permitindo o estorno do mesmo.");
            }


            //Estornar Pagamentos do Titulo
            if (PagamentosQuitadosTitulo(titulo).Count > 0)
            {
                _negocioPagamento = new NegocioPagamento();

                foreach (Pagamento pagamento in PagamentosQuitadosTitulo(titulo))
                {
                    _negocioPagamento.EstornarPagamento(titulo, pagamento, usuarioPermissao);
                }
            }

            //Data e Situação de estornado no Titulo
            titulo.DataExtorno = DateTime.Now.Date;
            titulo.EnumSituacaoMovimento = EnumSituacaoMovimento.Extornado;
            
            return Atualizar(titulo,usuarioPermissao);
        }

        public Pagamento UltimoPagamentoTitulo(Titulo titulo)
        {
            Pagamento ultimoPagamento = null;
            //Pegar o ultimo Pagamento
            if ((titulo.ListaPagamentos != null) && (titulo.ListaPagamentos.Count > 0))
            {
                //Ultimo Pagamento Quitado

                List<Pagamento> lista = titulo.ListaPagamentos.Cast<Pagamento>().ToList();
                ultimoPagamento = lista.Where(x => x.EnumSituacaoMovimento == EnumSituacaoMovimento.Quitado).LastOrDefault();
               
                //ultimoPagamento = titulo.ListaPagamentos[titulo.ListaPagamentos.Count - 1] as Pagamento;
            }
            return ultimoPagamento;
        }

        public List<Pagamento> PagamentosQuitadosTitulo(Titulo titulo)
        {
            List<Pagamento> lista = new List<Pagamento>();
            if ((titulo.ListaPagamentos != null) && (titulo.ListaPagamentos.Count > 0))
            {

                lista = titulo.ListaPagamentos.Cast<Pagamento>().ToList()
                    .Where(x => x.EnumSituacaoMovimento == EnumSituacaoMovimento.Quitado).ToList();

            }
            return lista;
        }

        public void Excluir(Titulo titulo, Usuario usuarioPermissao)
        {
            Titulo tituloTmp = ObterPorId(titulo.MovimentoId);
            _repositorioTitulo.Excluir(tituloTmp);
        }

        public IList<Titulo> ObterTodos()
        {
            return _repositorioTitulo.ObterTodos();
        }

        public IList<Titulo> ObterTodos(int inicio, int quantidade, int quantidadeMinima, 
            string ipte, string nossoNumero, Usuario usuarioPermissao, EnumTipoMovimentoFinanceiro enumTipoMovimentoFinanceiro, 
            string documento, long numeroMovimento, long numeroTitulo, string descricao, EnumSituacaoMovimento enumSituacaoMovimento,
            bool parcelado, Pessoa credorDevedor, Setor setor, FonteGeradora fonteGeradora, ContaContabil contaContabil, 
            DateTime? emissaoDe, DateTime? emissaoAte, DateTime? vencimentoDe, DateTime? vencimentoAte, DateTime? aceitoDe, 
            DateTime? aceitoAte, DateTime? quitadoDe, DateTime? quitadoAte, DateTime? estornadoDe, DateTime? estornadoAte, 
            decimal valorDe, decimal valorAte, FormaPagamento formaPagamento, TipoFormaPagamento tipoFormaPagamento)
        {
            IEnumerable<Titulo> enumerable =
            _repositorioTitulo.ObterTodos(inicio, quantidade, quantidadeMinima, ipte, nossoNumero, enumTipoMovimentoFinanceiro, documento, numeroMovimento, numeroTitulo, descricao, enumSituacaoMovimento, parcelado, credorDevedor, setor, fonteGeradora, contaContabil, emissaoDe, emissaoAte, vencimentoDe, vencimentoAte, aceitoDe, aceitoAte, quitadoDe, quitadoAte, estornadoDe, estornadoAte, valorDe, valorAte, formaPagamento, tipoFormaPagamento).ToList().Distinct();

            return enumerable.ToList();
        }

        public Titulo ObterPorId(long id)
        {
            return _repositorioTitulo.ObterPorId(id);
        }

        private Titulo AdicionarPagamento(Titulo titulo, ContaBanco contaBanco, FormaPagamento formaPagamento, decimal valor)
        {
            IList<Pagamento> pagamentosEfetivados = new List<Pagamento>();
            foreach (Pagamento pagamentosEfetivado in titulo.ListaPagamentos)
            {
                pagamentosEfetivados.Add(pagamentosEfetivado);
            }

            titulo.ListaPagamentos = new List<Pagamento>();
            foreach (Pagamento pagamentosEfetivado in pagamentosEfetivados)
            {
                titulo.AdicionarPagamento(pagamentosEfetivado);
            }

            Pagamento pagamento = new Pagamento();
            pagamento.FormaPagamento = formaPagamento;
            pagamento.ContaBanco = contaBanco;
            pagamento.DataLancamentoPagamento = DateTime.Now;
            pagamento.DataPagamento = titulo.DataVencimento;
            pagamento.Documento = titulo.Documento;
            pagamento.EnumSituacaoMovimento = EnumSituacaoMovimento.Aberto;
            pagamento.Titulo = titulo;
            pagamento.Valor = valor;

            titulo.AdicionarPagamento(pagamento);

            return titulo;
        }

        public string GeracaoBoleto(Titulo titulo, Pessoa sacado, ContaBanco contaBanco, FormaPagamento formaPagamento, Usuario usuarioPermissao)
        {
            if(titulo.EnumSituacaoMovimento == EnumSituacaoMovimento.Quitado)
            {
                throw new Exception("Não é possível gerar Boleto para um Título quitado!");
            }

            if (titulo.EnumSituacaoMovimento == EnumSituacaoMovimento.Extornado)
            {
                throw new Exception("Não é possível gerar Boleto para um Título extornado!");
            }

            Pagamento pagamentoCadastrado = titulo.ListaPagamentos.Cast<Pagamento>().FirstOrDefault(
                                        x => x.FormaPagamento.TipoFormaPagamento.EnumTipoFormaPagamento == EnumTipoFormaPagamento.Boleto);
            if(pagamentoCadastrado != null && pagamentoCadastrado.FormaPagamento.FormaPagamentoId != formaPagamento.FormaPagamentoId)
            {
                throw new Exception("Não é possível gerar Boleto para um Título que já possua um Boleto de outra Forma de Pagamento!" 
                                    + Environment.NewLine + "Extorne a Forma de Pagamento Boleto deste Título para inserir uma nova.");
            }

            decimal valor = ValorSaldoTitulo(titulo);

            GeracaoBoleto geracaoBoleto = new GeracaoBoleto(titulo, sacado, contaBanco, formaPagamento, valor);
            string retorno = geracaoBoleto.GerarBoletoArquivo();

            foreach (BoletoNet.BoletoBancario boletoBancario in geracaoBoleto.ListaBoletosBancario)
            {
                if (string.IsNullOrEmpty(titulo.Ipte) || !titulo.Ipte.Equals(boletoBancario.Boleto.CodigoBarra.LinhaDigitavel))
                {
                    titulo.Ipte = boletoBancario.Boleto.CodigoBarra.LinhaDigitavel;
                    titulo.NossoNumero = boletoBancario.Boleto.NossoNumero;
                    titulo = AdicionarPagamento(titulo, contaBanco, formaPagamento, valor);

                    Atualizar(titulo, usuarioPermissao);
                }
            }

            return retorno;
        }
    }
}